import numpy as np
from scipy.sparse import linalg as sLA 
import graph_tool as gt
import graph_tool.spectral as gts
from old_gt import gt_graph_io

def get_degrees_dic(g):
    degs = {}
    for v in g.vertices():
        index = int(v)
        deg = v.out_degree()
        degs[index] = deg
    return degs

def cn(graph):
    """
        Calculates the Common Neighbour similarity index of a graph.
    """
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = paths
    return S
    
def salton(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i] 
            j_degree = degrees[j] 
            factor = i_degree * j_degree
            S[i,j] = (1.0/np.sqrt(factor)) * paths[i,j]
    return S
    
def jacard(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            if j != i:
                i_degree = degrees[i] 
                j_degree = degrees[j] 
                factor = i_degree + j_degree - paths[i,j]
                S[i,j] = (1.0/factor) * paths[i,j]
    return S
    
def sorensen(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i] 
            j_degree = degrees[j] 
            factor = i_degree + j_degree
            S[i,j] = (2.0/factor) * paths[i,j]
    return S
    
def hpi(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i] 
            j_degree = degrees[j] 
            factor = np.min((i_degree, j_degree))
            S[i,j] = (2.0/factor) * paths[i,j]
    return S
    
def hdi(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i]
            j_degree = degrees[j]
            factor = np.max((i_degree, j_degree))
            S[i,j] = (2.0/factor) * paths[i,j]
    return S
    
def lhn1(graph):
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    paths = A**2
    paths = paths.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i] 
            j_degree = degrees[j] 
            factor = i_degree * j_degree
            S[i,j] = (1.0/factor) * paths[i,j]
    return S
    
    
def pa(graph):
    """
        Calculates Preferential Attachment index.
        
        Returns S the similarity matrix.
    """
    N = graph.num_vertices()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        for j in xrange(N):
            i_degree = degrees[i] 
            j_degree = degrees[j] 
            factor = i_degree * j_degree
            S[i,j] = factor
    return S
    
def katz(graph, h):
    """
        Calculates the Katz index.
        
        Parameters:
        graph: the graph.
        h: lambda, the normalization factor that must be 0 < h < 1
    """
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    I = np.identity(N)
    to_inv = (I - h*A)
    S = np.linalg.inv(to_inv) - I
    S = np.asarray(S)
    return S
    
def katz_it_version(graph, h):
    """
        h: is lambda
    """
    A = gts.adjacency(graph)
    S = np.zeros(A.shape)
    for l in xrange(1,400):
        factor = h**l
        paths = A ** l
        for i in xrange(S.shape[0]):
            for j in xrange(S.shape[0]):
                S[i,j] += factor * paths[i,j]
    return S

def katz_h_parameter(graph):
    """"
        Computes the reciprocal of the largest eigenvalue of the adjacency matrix of the graph.
        Theiprocal should be decreased so the reduced value can be used as the h parameter
        to compute the katz index.
    """
    A = gts.adjacency(graph)
    eigvals = sLA.eigsh(A, which='LM', return_eigenvectors=False)
    h = np.sort(eigvals)[-1]
    h = 1.0/h
    return h
    
def aa(graph):
    """
        Calculates the Adamic-Adar index.
        
    """
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    A = A.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        A_i = A[i]
        for j in xrange(N):
            if j != i:
                A_j = A[j]
                intersection = A_i + A_j
                common_ns_degs = list()
                for index in xrange(N):
                    if intersection[index] == 2:
                        cn_deg = degrees[index]
                        common_ns_degs.append(1.0/np.log10(cn_deg))
                S[i,j] = np.sum(common_ns_degs)
    return S
    
def ra(graph):
    """
        Calculates resource allocation.
        
    """
    N = graph.num_vertices()
    A = gts.adjacency(graph)
    A = A.toarray()
    S = np.zeros((N,N))
    degrees = get_degrees_dic(graph)
    for i in xrange(N):
        A_i = A[i]
        for j in xrange(N):
            if j != i:
                A_j = A[j]
                intersection = A_i + A_j
                common_ns_degs = list()
                for index in xrange(N):
                    if intersection[index] == 2:
                        cn_deg = degrees[index]
                        common_ns_degs.append(1.0/cn_deg)
                S[i,j] = np.sum(common_ns_degs)
    return S

def lp(graph, h):
    """
        Calculates the Local Path Index.
        
        Parameters:
        h: lambda, the katz normalization factor.
        
    """ 
    A = gts.adjacency(graph)
    paths_len2 = A**2
    paths_len3 = A**3
    S = paths_len2 + (h * paths_len3)
    
    return S.toarray()
    
def test():
#     g = old_gt.Graph(directed=False)
#     vtext = g.new_vertex_property("string")
#     g.vp["vtext"] = vtext
#     v1 = g.add_vertex()
#     v2 = g.add_vertex()
#     v3 = g.add_vertex()
#     v4 = g.add_vertex()
#     g.add_edge(v1,v3)
#     vtext[v1] = "v1"
#     vtext[v2] = "v2"
#     vtext[v3] = "v3"
#     vtext[v4] = "v4"
#      
#     vfilter = g.new_vertex_property("bool")
#     vfilter[v1] = True
#     vfilter[v2] = False
#     vfilter[v3] = True
#     vfilter[v4] = True
#     g.set_vertex_filter(vfilter)

    file_path = "./edges.txt"
    graph = gt_graph_io.read_graph(file_path)
#     print katz_h_parameter(graph.g)
#     S = katz_it_version(graph.g, 0.3)
#     S2 = katz(graph.g, 0.3)
    
    S = lp(graph.g,0.3)
    print type(S)
    print S[graph.label_to_index['52'], graph.label_to_index['1']]
    print graph.label_to_index['52'], graph.label_to_index['1']
    
# gt_tests()